﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace Fysh.ComputerScience.Genetics
{
    public class EntrezSequenceAqcuisition
    {
        private IEntrezServiceProvider _entrezServiceProvider;
        
        public EntrezSequenceAqcuisition(IEntrezServiceProvider entrezServiceProvider)
        {
            _entrezServiceProvider = entrezServiceProvider;
        }
        
        /// <summary>
        /// Duration in seconds before a search or download request times out
        /// </summary>
        public int RequestTimeout
        {
            get
            {
                throw new System.NotImplementedException();
            }
            set
            {
            }
        }
        /// <summary>
        /// Searches the taxonomy database
        /// </summary>
        public void SearchTaxonomy(string searchTerms, TaxonomySearchCallback callback)
        {
            ThreadPool.QueueUserWorkItem(delegate(object notused) { AsyncSearchTaxonomy(searchTerms, callback); });
        }

        private void AsyncSearchTaxonomy(string searchTerms, TaxonomySearchCallback callback)
        {
            //GC.Collect();
            //GC.WaitForPendingFinalizers();
            try
            {
                eUtilsService.eUtilsServiceSoapClient utilsServerConnection = _entrezServiceProvider.CreateUtilsClient();
                eUtilsService.eSearchRequest searchRequest = new Fysh.ComputerScience.Genetics.eUtilsService.eSearchRequest();

                searchRequest.db = "taxonomy";
                searchRequest.term = searchTerms;
                searchRequest.RetMax = "100";

                eUtilsService.eSearchResult searchResult = utilsServerConnection.run_eSearch(searchRequest);

                eFetchTaxonService.eUtilsServiceSoapClient taxonServerConnection = new Fysh.ComputerScience.Genetics.eFetchTaxonService.eUtilsServiceSoapClient();
                eFetchTaxonService.eFetchRequest taxonRequest = new Fysh.ComputerScience.Genetics.eFetchTaxonService.eFetchRequest();

                Species[] speciesCollection = new Species[searchResult.IdList.Length];

                for (int i = 0; i < searchResult.IdList.Length; i++)
                {
                    taxonRequest.id = searchResult.IdList[i];

                    eFetchTaxonService.eFetchResult taxonResult = taxonServerConnection.run_eFetch(taxonRequest);

                    speciesCollection[i].TaxaId = searchResult.IdList[i];
                    speciesCollection[i].ScientificName = taxonResult.TaxaSet[0].ScientificName;
                    speciesCollection[i].CommonName = taxonResult.TaxaSet[0].OtherNames.GenbankCommonName;
                }

                callback(speciesCollection);
            }
            catch (Exception ex)
            {
            }
        }

        public void SearchSequenceDatabase(Species species, EntrezDatabase sourceDatabase, SequenceSearchCallback callback)
        {
            ThreadPool.QueueUserWorkItem(delegate { AsyncSearchSequenceDatabase(species, sourceDatabase, callback); });
        }

        private void AsyncSearchSequenceDatabase(Species species, EntrezDatabase sourceDatabase, SequenceSearchCallback callback)
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
            
            eUtilsService.eUtilsServiceSoapClient utilsServerConnection = new Fysh.ComputerScience.Genetics.eUtilsService.eUtilsServiceSoapClient();
            eUtilsService.eSearchRequest searchRequest = new Fysh.ComputerScience.Genetics.eUtilsService.eSearchRequest();

            searchRequest.db = sourceDatabase.ToString();
            searchRequest.term = "txid" + species.TaxaId;
            searchRequest.RetMax = "100";

            eUtilsService.eSearchResult searchResult = utilsServerConnection.run_eSearch(searchRequest);

            species.AvailableSequences = new Sequence[searchResult.IdList.Length];

            for (int i = 0; i < searchResult.IdList.Length; i++)
            {
                eUtilsService.eSummaryRequest summaryRequest = new Fysh.ComputerScience.Genetics.eUtilsService.eSummaryRequest();
                
                summaryRequest.db = sourceDatabase.ToString();
                summaryRequest.id = searchResult.IdList[i];
                summaryRequest.retmax = "1";

                eUtilsService.eSummaryResult summaryResult = utilsServerConnection.run_eSummary(summaryRequest);

                species.AvailableSequences[i].DatabaseId = searchResult.IdList[i];
                species.AvailableSequences[i].AccessionNumber = summaryResult.DocSum[0].Item[0].ItemContent;
                species.AvailableSequences[i].Description = summaryResult.DocSum[0].Item[1].ItemContent;
                species.AvailableSequences[i].Gi = summaryResult.DocSum[0].Item[3].ItemContent;
                species.AvailableSequences[i].GlobalLength = Convert.ToInt32(summaryResult.DocSum[0].Item[8].ItemContent);
                species.AvailableSequences[i].Database = sourceDatabase;
            }

            GC.Collect();
            GC.WaitForPendingFinalizers();

            callback(species);
        }

        public void DownloadSequence(Species species, int sequenceIndex, EntrezDatabase sourceDatabase, SequenceDownloadCallback callback)
        {
            ThreadPool.QueueUserWorkItem(delegate { AsyncDownloadSequence(species, sequenceIndex, callback, 0, species.AvailableSequences[sequenceIndex].GlobalLength); });
        }

        public void DownloadSequence(Species species, int sequenceIndex, SequenceDownloadCallback callback, int sequenceStart, int sequenceLength)
        {
            ThreadPool.QueueUserWorkItem(delegate { AsyncDownloadSequence(species, sequenceIndex, callback, sequenceStart, sequenceLength); });
        }

        private void AsyncDownloadSequence(Species species, int sequenceIndex, SequenceDownloadCallback callback, int sequenceStart, int sequenceLength)
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
            
            StringBuilder sequenceBuilder = new StringBuilder();
            
            eFetchSequenceService.eUtilsServiceSoapClient sequenceServer = new Fysh.ComputerScience.Genetics.eFetchSequenceService.eUtilsServiceSoapClient();
            eFetchSequenceService.eFetchRequest sequenceRequest = new Fysh.ComputerScience.Genetics.eFetchSequenceService.eFetchRequest();

            sequenceRequest.id = species.AvailableSequences[sequenceIndex].DatabaseId;
            sequenceRequest.db = species.AvailableSequences[sequenceIndex].Database.ToString();

            int downloadParts = (sequenceLength - sequenceStart) / 500;

            for (int i = 0; i < downloadParts; i++)
            {
                sequenceRequest.seq_start = (sequenceStart + i * 500).ToString();
                sequenceRequest.seq_stop = (sequenceStart + (i + 1) * 500).ToString();
                sequenceBuilder.Append(sequenceServer.run_eFetch(sequenceRequest).GBSet.GBSeq[0].GBSeq_sequence);
            }

            sequenceRequest.seq_start = (sequenceStart + (downloadParts * 500) + ((sequenceLength - sequenceStart) % 500)).ToString();
            sequenceRequest.seq_stop = (sequenceStart + sequenceLength).ToString();
            sequenceBuilder.Append(sequenceServer.run_eFetch(sequenceRequest).GBSet.GBSeq[0].GBSeq_sequence);

            species.AvailableSequences[sequenceIndex].SequenceCharacters = sequenceBuilder.ToString();
            species.AvailableSequences[sequenceIndex].StartSequence = sequenceStart.ToString();

            GC.Collect();
            GC.WaitForPendingFinalizers();

            callback(species.AvailableSequences[sequenceIndex]);
        }
    }
}
